﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Bettzueche.RLGlue.Generics;
using generic = Bettzueche.RLGlue.Generics;

namespace Bettzueche.RLGlue.Codec.Network {

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="O"></typeparam>
    /// <typeparam name="A"></typeparam>
    class GenericClientEnvironment<O,A> : AbstractClientEnvironment
        where A : struct
        where O : struct
    {
        /// <summary>Referenz auf die Environment Implementierung.</summary>
        protected IGenericEnvironment<O,A> env;

        /// <summary>
        /// New Middleware connection to RLGlue.
        /// </summary>
        /// <param name="envmnt">Your Environment.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public GenericClientEnvironment(IGenericEnvironment<O,A> envmnt) {
            if (envmnt == null)
                throw new ArgumentNullException();
            this.env = envmnt;
            this.network = new Network();
        }

        #region RMI-Handler

        /// <summary>
        /// Handler für eine EnvInit Nachricht von RLGlue.exe.
        /// </summary>
        /// <remarks>
        /// Auslesen des Parameters (task_spec string) und env Methode aufrufen.
        /// Bestätigung des Aufrufs mit Rückgabewert (taks_spec string) vorbereiten.
        /// </remarks>
        protected override void onEnvInit() {
            String taskSpec = env.Env_init();

            network.ClearSendBuffer();
            network.PutInt(Protocol.kEnvInit);
            network.PutInt(Network.SizeOf(taskSpec)); // This is different than taskSpec.length(). It also includes
            network.PutString(taskSpec);
        }

        /// <summary>
        /// Handler für eine EnvStart Nachricht von RLGlue.exe.
        /// </summary>
        /// <remarks>
        /// Auslesen des Parameters (void) entfällt. Env Methode aufrufen.
        /// Bestätigung des Aufrufs mit Rückgabewert (observation) vorbereiten.
        /// </remarks>
        protected override void onEnvStart() {
            Observation<O> obs = env.Env_start();

            network.ClearSendBuffer();
            network.PutInt(Protocol.kEnvStart);
            network.PutInt(Network.SizeOf(obs));
            network.PutObservation(obs);
        }

        /// <summary>
        /// Handler für eine EnvStep Nachricht von RLGlue.exe.
        /// </summary>
        /// <remarks>
        /// Auslesen des Parameters (action) und Env Methode aufrufen.
        /// Bestätigung des Aufrufs mit Rückgabewert (R.O.T.) vorbereiten.
        /// </remarks>
        protected override void onEnvStep() {
            generic.Action<A> action = network.GetAction<A>();
            Reward_Observation_Terminal<O> rewardObservation = env.Env_step(action);

            network.ClearSendBuffer();
            network.PutInt(Protocol.kEnvStep);
            network.PutInt(Network.SizeOf(rewardObservation));

            network.PutRewardObservation(rewardObservation);
        }

        /// <summary>
        /// Handler für eine EnvCleanup Nachricht von RLGlue.exe.
        /// </summary>
        /// <remarks>
        /// Auslesen des Parameters (void) entfällt. Env Methode aufrufen.
        /// Bestätigung des Aufrufs mit Rückgabewert (void) vorbereiten.
        /// </remarks>
        protected override void onEnvCleanup() {
            env.Env_cleanup();
            network.ClearSendBuffer();
            network.PutInt(Protocol.kEnvCleanup);
            network.PutInt(0);
        }

        /// <summary>
        /// Handler für eine EnvMessage Nachricht von RLGlue.exe.
        /// </summary>
        /// <remarks>
        /// Auslesen des Parameters (message string) und Env Methode aufrufen.
        /// Bestätigung des Aufrufs mit Rückgabewert (reply string) vorbereiten.
        /// </remarks>
        protected override void onEnvMessage() {
            String message = network.GetString();
            String reply = env.env_message(message);

            network.ClearSendBuffer();
            network.PutInt(Protocol.kEnvMessage);
            network.PutInt(Network.SizeOf(reply));
            network.PutString(reply);
        }

        #endregion
    }
}
